package org.ims.inv.component.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
import org.ims.inv.dao.system.DAOAppLog;
import org.ims.inv.model.system.ModelAppLog;
import org.ims.inv.web.util.ContextUtil;

import cn.trymore.core.exception.DAOException;
import cn.trymore.core.log.LogAnnotation;
import cn.trymore.core.util.UtilApp;
import cn.trymore.core.util.UtilCollection;
import cn.trymore.core.util.UtilString;

/**
 * 系统日志AOP控制与日志记录.
 * 
 * @author Jeccy.Zhao
 *
 */
public class LogAdvice
implements MethodInterceptor
{
	
	/**
	 * The LOGGER
	 */
	private static final Logger LOGGER = Logger.getLogger(LogAdvice.class);
	
	/**
	 * Repository of system log
	 */
	private DAOAppLog daoAppLog;
	
	/*
	 * (non-Javadoc)
	 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
	 */
	@Override
	public Object invoke(MethodInvocation method) throws Throwable
	{
		ModelAppLog entity = new ModelAppLog();
		
		long procTime = System.currentTimeMillis();
		HttpServletRequest request = null;
		
		try
		{
			if (method.getMethod().getDeclaringClass() == org.apache.struts.actions.DispatchAction.class)
			{
				Object[] args = method.getArguments();
				if (args != null && args.length > 0)
				{
					String action = null;
					for (Object arg : args)
					{
						if (arg instanceof HttpServletRequest)
						{
							request = (HttpServletRequest) arg;
							entity.setLogHost(UtilApp.getRequetIpAddr(request));
							action = request.getParameter("action");
							break;
						}
					}
					
					// obtains annotation properties
					if (UtilString.isNotEmpty(action))
					{
						Method targetMethod = getDeclaredMethod(method, action);
						
						if (targetMethod != null)
						{
							boolean isLogAnnotation = targetMethod.isAnnotationPresent(LogAnnotation.class);
							if (isLogAnnotation)
							{
								LogAnnotation logAnnotation = targetMethod.getAnnotation(LogAnnotation.class);
								entity.setLogDesc(this.evalDescription(logAnnotation.description(), request));
								entity.setLogType(logAnnotation.type());
								
								String methodClassName = method.getThis().getClass().toString(); 
								entity.setLogActionMethod(methodClassName + "." + request.getParameter("action"));
							}
						}
					}
				}
			}
			
			Object result = method.proceed();
			return result;
		}
		finally
		{
			// ensures the logged method invoked
			if (UtilString.isNotEmpty(entity.getLogDesc()))
			{
				procTime = System.currentTimeMillis() - procTime;
				try
				{
					entity.setLogProcTime(procTime);
					entity.setLogCreationTime(new Date());
					
					if (ContextUtil.getCurrentUser() != null)
					{
						entity.setLogOperator(ContextUtil.getCurrentUser().getUsername());
					}
					
					this.daoAppLog.saveOrUpdate(entity);
				}
				catch (DAOException e)
				{
					LOGGER.error("Exception raised when recording log:", e);
				}
			}
		}
	}
	
	/**
	 * Obtains declared method 
	 * 
	 * @param object
	 * @param methodName
	 * @param parameterTypes
	 * @return
	 * @throws NoSuchMethodException 
	 * @throws SecurityException 
	 */
	private static Method getDeclaredMethod (MethodInvocation method, String methodName)
	{
		Class<?> clazz = method.getThis().getClass();
		while (clazz !=  Object.class)
		{
			try
			{
				Method targetMethod = clazz.getDeclaredMethod(
						methodName, method.getMethod().getParameterTypes());
				
				if (targetMethod != null)
				{
					return targetMethod;
				}
			}
			catch (Exception e)
			{
				// DO NOTHING HERE
			}
			
			clazz = clazz.getSuperclass();
		}
		
		return null;
	}
	
	/**
	 * Evaluation the description 
	 * 
	 * @param description
	 * @param request
	 * @return
	 */
	private String evalDescription (String description, HttpServletRequest request)
	{
		if (request != null)
		{
			List<String> params = getParameterNamesFromText(description);
			if (UtilCollection.isNotEmpty(params))
			{
				for (String paramName : params)
				{
					String paramValue = request.getParameter(paramName);
					if (paramValue != null)
					{
						description = description.replace("${" + paramName + "}", paramValue);
					}
				}
			}
		}
		
		return description;
	}
	
	/**
	 * Obtains parameter name from the specified text
	 * 
	 * @param text
	 * @return
	 */
	private List<String> getParameterNamesFromText (String text)
	{
		List<String> params = null;
		
		int startPos = 0;
		while (startPos > -1)
		{
			startPos = text.indexOf("${", startPos);
			if (startPos > -1)
			{
				int tailPos = text.indexOf("}", startPos);
				if (tailPos > -1)
				{
					if (params == null)
					{
						params = new ArrayList<String>();
					}
					
					params.add(text.substring(startPos + "${".length(), tailPos));
				}
				
				startPos = tailPos;
			}
		}
		
		return params;
	}
	
	public static Logger getLogger()
	{
		return LOGGER;
	}

	public void setDaoAppLog(DAOAppLog daoAppLog)
	{
		this.daoAppLog = daoAppLog;
	}

	public DAOAppLog getDaoAppLog()
	{
		return daoAppLog;
	}

}
